package query_handler

import (
	"crypto/sha256"
	"encoding/hex"
	"file-cleaner/internal/logger"
	"file-cleaner/internal/pkg/file_cleaner/file_info"
	"os"
	"syscall"
	"time"
)

type QueryHandler interface {
	doHandle(info *file_info.FileInfo) bool
}

type QueryByATimeImpl struct {
	AfterTime  time.Time
	BeforeTime time.Time
}

type QueryByCTimeImpl struct {
	AfterTime  time.Time
	BeforeTime time.Time
}

type QueryByMTimeImpl struct {
	AfterTime  time.Time
	BeforeTime time.Time
}

func (q QueryByATimeImpl) doHandle(info *file_info.FileInfo) bool {
	stat, ok := info.Sys().(*syscall.Stat_t)
	if !ok {
		logger.Warnf("Failed to parse stat from FileInfo: %v", info)
		return false
	}
	atime := stat.Atim
	tt := time.Unix(atime.Sec, atime.Nsec)
	return tt.After(q.AfterTime) && tt.Before(q.BeforeTime)
}

func (q QueryByCTimeImpl) doHandle(info *file_info.FileInfo) bool {
	stat, ok := info.Sys().(*syscall.Stat_t)
	if !ok {
		logger.Warnf("Failed to parse stat from FileInfo: %v", info)
		return false
	}
	ctime := stat.Ctim
	tt := time.Unix(ctime.Sec, ctime.Nsec)
	return tt.After(q.AfterTime) && tt.Before(q.BeforeTime)
}

func (q QueryByMTimeImpl) doHandle(info *file_info.FileInfo) bool {
	stat, ok := info.Sys().(*syscall.Stat_t)
	if !ok {
		logger.Warnf("Failed to parse stat from FileInfo: %v", info)
		return false
	}
	mtime := stat.Mtim
	tt := time.Unix(mtime.Sec, mtime.Nsec)
	return tt.After(q.AfterTime) && tt.Before(q.BeforeTime)
}

type QueryHashImpl struct {
}

func (q QueryHashImpl) doHandle(info *file_info.FileInfo) bool {
	bfile, err := os.ReadFile(info.Path())
	if err != nil {
		logger.Error(err)
		return false
	}
	info.SetHash(sha256Hash(bfile))
	return true
}

func sha256Hash(data []byte) string {
	m := sha256.New()
	m.Write(data)
	return hex.EncodeToString(m.Sum(nil))
}

type QueryForDeleteOnlyImpl struct {
}

func (d QueryForDeleteOnlyImpl) doHandle(info *file_info.FileInfo) bool {
	err := info.SetTag(file_info.OnlyDelete)
	if err != nil {
		logger.Errorf("Failed to set tag for file: %v, cased by: %v", info.Path(), err)
		return false
	}
	info.SetHash("OnlyDelete")
	return true
}
